﻿using System.Diagnostics;
using System.Threading.Tasks;
using Language.Utility;
using Defines = Red.Scripts.Defines.Scc;

namespace Language.Scc
{
    internal class SolutionParser : Exclusive
    {
        #region Private Data

        private Defines.Connection _connection;
        private Stub.Database _database;

        #endregion Private Data

        #region Public Properties

        public int InitializationMessagesRecieved { get; private set; }
        public bool InitializationComplete { get; private set; }
        public bool InitializationSent { get; private set; }

        public TaskCompletionSource<bool> ParseTask { get; private set; }

        #endregion Public Properties

        #region C-Tor

        public SolutionParser( Defines.Connection connection, Stub.Database database )
        {
            _connection = connection;
            _database = database;

            Reset();
        }

        #endregion C-Tor

        #region Public Methods

        public async Task RequestParse( string anyScriptFilePath )
        {
            await RunLockedWait( anyScriptFilePath );
        }

        public void Reset()
        {
            InitializationMessagesRecieved = 0;
            InitializationComplete = false;
            InitializationSent = false;
            ParseTask = new TaskCompletionSource<bool>();
            _connection.MessageHandlers[ comm.SccInitializeResponse.GetStaticID() ] = OnInitializeResponseRecieved;

            _database.Clear();
        }

        #endregion Public Methods

        #region Private Methods

        private void ParseInternal( string anyScriptFilePath )
        {
            if ( InitializationComplete )
                return;

            if ( InitializationSent )
                return;

            if ( string.IsNullOrWhiteSpace( anyScriptFilePath ) )
                return;

            string root = Project.FindRoot( anyScriptFilePath );
            if ( string.IsNullOrEmpty( root ) )
                return;

            Debug.Assert( InitializationMessagesRecieved == 0 );

            comm.SccInitialize msg = new comm.SccInitialize()
            {
                inputDirectory = root
            };

            _connection.Send( msg );

            InitializationSent = true;
        }

        #endregion Private Methods

        #region Event Handlers

        private void OnInitializeResponseRecieved( comm.Message message )
        {
            comm.SccInitializeResponse response = (comm.SccInitializeResponse)message;

            Debug.Print( $"SccInitializeResponse Part {response.count}/{response.total} received" );
            Debug.Assert( InitializationMessagesRecieved < response.total, "Too many initialization packets sent from scc" );
            Debug.Assert( response.count < response.total, "Invalid initialization message received from scc" );

            _database.Initialize( response.database );

            ++InitializationMessagesRecieved;
            InitializationComplete = InitializationMessagesRecieved == response.total;

            if ( InitializationComplete )
            {
                _database.InitializationComplete();
                _connection.MessageHandlers.Remove( comm.SccInitializeResponse.GetStaticID() );
                ParseTask.SetResult( true );
            }
        }

        #endregion Event Handlers

        #region Exclusive

        protected override async Task DoLocked( params object[] list )
        {
            await Task.Run( () =>
            {
                string anyScriptFilePath = (string)list[ 0 ];
                ParseInternal( anyScriptFilePath );
            } );
        }

        #endregion Exclusive
    }
}
